home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 1 / Cream of the Crop 1.iso / PROGRAM / RCS_56.ARJ / RCSUTIL.C < prev    next >
C/C++ Source or Header  |  1992-02-11  |  20KB  |  995 lines

  1. /*
  2.  *                     RCS utilities
  3.  */
  4.  
  5. /* Copyright (C) 1982, 1988, 1989 Walter Tichy
  6.    Copyright 1990, 1991 by Paul Eggert
  7.    Distributed under license by the Free Software Foundation, Inc.
  8.  
  9. This file is part of RCS.
  10.  
  11. RCS is free software; you can redistribute it and/or modify
  12. it under the terms of the GNU General Public License as published by
  13. the Free Software Foundation; either version 2, or (at your option)
  14. any later version.
  15.  
  16. RCS is distributed in the hope that it will be useful,
  17. but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19. GNU General Public License for more details.
  20.  
  21. You should have received a copy of the GNU General Public License
  22. along with RCS; see the file COPYING.  If not, write to
  23. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  24.  
  25. Report problems and direct all questions to:
  26.  
  27.     rcs-bugs@cs.purdue.edu
  28.  
  29. */
  30.  
  31.  
  32.  
  33.  
  34. /* $Log: rcsutil.c,v $
  35.  * Revision 5.10  1991/10/07  17:32:46  eggert
  36.  * Support piece tables even if !has_mmap.
  37.  *
  38.  * Revision 5.9  1991/08/19  03:13:55  eggert
  39.  * Add spawn() support.  Explicate assumptions about getting invoker's name.
  40.  * Standardize user-visible dates.  Tune.
  41.  *
  42.  * Revision 5.8  1991/04/21  11:58:30  eggert
  43.  * Plug setuid security hole.
  44.  *
  45.  * Revision 5.6  1991/02/26  17:48:39  eggert
  46.  * Fix setuid bug.  Use fread, fwrite more portably.
  47.  * Support waitpid.  Don't assume -1 is acceptable to W* macros.
  48.  * strsave -> str_save (DG/UX name clash)
  49.  *
  50.  * Revision 5.5  1990/12/04  05:18:49  eggert
  51.  * Don't output a blank line after a signal diagnostic.
  52.  * Use -I for prompts and -q for diagnostics.
  53.  *
  54.  * Revision 5.4  1990/11/01  05:03:53  eggert
  55.  * Remove unneeded setid check.  Add awrite(), fremember().
  56.  *
  57.  * Revision 5.3  1990/10/06  00:16:45  eggert
  58.  * Don't fread F if feof(F).
  59.  *
  60.  * Revision 5.2  1990/09/04  08:02:31  eggert
  61.  * Store fread()'s result in an fread_type object.
  62.  *
  63.  * Revision 5.1  1990/08/29  07:14:07  eggert
  64.  * Declare getpwuid() more carefully.
  65.  *
  66.  * Revision 5.0  1990/08/22  08:13:46  eggert
  67.  * Add setuid support.  Permit multiple locks per user.
  68.  * Remove compile-time limits; use malloc instead.
  69.  * Switch to GMT.  Permit dates past 1999/12/31.
  70.  * Add -V.  Remove snooping.  Ansify and Posixate.
  71.  * Tune.  Some USG hosts define NSIG but not sys_siglist.
  72.  * Don't run /bin/sh if it's hopeless.
  73.  * Don't leave garbage behind if the output is an empty pipe.
  74.  * Clean up after SIGXCPU or SIGXFSZ.  Print name of signal that caused cleanup.
  75.  *
  76.  * Revision 4.6  89/05/01  15:13:40  narten
  77.  * changed copyright header to reflect current distribution rules
  78.  * 
  79.  * Revision 4.5  88/11/08  16:01:02  narten
  80.  * corrected use of varargs routines
  81.  * 
  82.  * Revision 4.4  88/08/09  19:13:24  eggert
  83.  * Check for memory exhaustion.
  84.  * Permit signal handlers to yield either 'void' or 'int'; fix oldSIGINT botch.
  85.  * Use execv(), not system(); yield exit status like diff(1)'s.
  86.  * 
  87.  * Revision 4.3  87/10/18  10:40:22  narten
  88.  * Updating version numbers. Changes relative to 1.1 actually
  89.  * relative to 4.1
  90.  * 
  91.  * Revision 1.3  87/09/24  14:01:01  narten
  92.  * Sources now pass through lint (if you ignore printf/sprintf/fprintf 
  93.  * warnings)
  94.  * 
  95.  * Revision 1.2  87/03/27  14:22:43  jenkins
  96.  * Port to suns
  97.  * 
  98.  * Revision 4.1  83/05/10  15:53:13  wft
  99.  * Added getcaller() and findlock().
  100.  * Changed catchints() to check SIGINT for SIG_IGN before setting up the signal
  101.  * (needed for background jobs in older shells). Added restoreints().
  102.  * Removed printing of full RCS path from logcommand().
  103.  * 
  104.  * Revision 3.8  83/02/15  15:41:49  wft
  105.  * Added routine fastcopy() to copy remainder of a file in blocks.
  106.  *
  107.  * Revision 3.7  82/12/24  15:25:19  wft
  108.  * added catchints(), ignoreints() for catching and ingnoring interrupts;
  109.  * fixed catchsig().
  110.  *
  111.  * Revision 3.6  82/12/08  21:52:05  wft
  112.  * Using DATEFORM to format dates.
  113.  *
  114.  * Revision 3.5  82/12/04  18:20:49  wft
  115.  * Replaced SNOOPDIR with SNOOPFILE; changed addlock() to update
  116.  * lockedby-field.
  117.  *
  118.  * Revision 3.4  82/12/03  17:17:43  wft
  119.  * Added check to addlock() ensuring only one lock per person.
  120.  * Addlock also returns a pointer to the lock created. Deleted fancydate().
  121.  *
  122.  * Revision 3.3  82/11/27  12:24:37  wft
  123.  * moved rmsema(), trysema(), trydiraccess(), getfullRCSname() to rcsfnms.c.
  124.  * Introduced macro SNOOP so that snoop can be placed in directory other than
  125.  * TARGETDIR. Changed %02d to %.2d for compatibility reasons.
  126.  *
  127.  * Revision 3.2  82/10/18  21:15:11  wft
  128.  * added function getfullRCSname().
  129.  *
  130.  * Revision 3.1  82/10/13  16:17:37  wft
  131.  * Cleanup message is now suppressed in quiet mode.
  132.  */
  133.  
  134.  
  135.  
  136.  
  137. #include "rcsbase.h"
  138.  
  139. libId(utilId, "$Id: rcsutil.c,v 5.10 1991/10/07 17:32:46 eggert Exp $")
  140.  
  141. #if !has_memcmp
  142.     int
  143. memcmp(s1, s2, n)
  144.     void const *s1, *s2;
  145.     size_t n;
  146. {
  147.     register unsigned char const
  148.         *p1 = (unsigned char const*)s1,
  149.         *p2 = (unsigned char const*)s2;
  150.     register size_t i = n;
  151.     register int r = 0;
  152.     while (i--  &&  !(r = (*p1++ - *p2++)))
  153.         ;
  154.     return r;
  155. }
  156. #endif
  157.  
  158. #if !has_memcpy
  159.     void *
  160. memcpy(s1, s2, n)
  161.     void *s1;
  162.     void const *s2;
  163.     size_t n;
  164. {
  165.     register char *p1 = (char*)s1;
  166.     register char const *p2 = (char const*)s2;
  167.     while (n--)
  168.         *p1++ = *p2++;
  169.     return s1;
  170. }
  171. #endif
  172.  
  173. #if lint
  174.     malloc_type lintalloc;
  175. #endif
  176.  
  177. /*
  178.  * list of blocks allocated with ftestalloc()
  179.  * These blocks can be freed by ffree when we're done with the current file.
  180.  * We could put the free block inside struct alloclist, rather than a pointer
  181.  * to the free block, but that would be less portable.
  182.  */
  183. struct alloclist {
  184.     malloc_type alloc;
  185.     struct alloclist *nextalloc;
  186. };
  187. static struct alloclist *alloced;
  188.  
  189.  
  190.     static malloc_type
  191. okalloc(p)
  192.     malloc_type p;
  193. {
  194.     if (!p)
  195.         faterror("out of memory");
  196.     return p;
  197. }
  198.  
  199.     malloc_type
  200. testalloc(size)
  201.     size_t size;
  202. /* Allocate a block, testing that the allocation succeeded.  */
  203. {
  204.     return okalloc(malloc(size));
  205. }
  206.  
  207.     malloc_type
  208. testrealloc(ptr, size)
  209.     malloc_type ptr;
  210.     size_t size;
  211. /* Reallocate a block, testing that the allocation succeeded.  */
  212. {
  213.     return okalloc(realloc(ptr, size));
  214. }
  215.  
  216.     malloc_type
  217. fremember(ptr)
  218.     malloc_type ptr;
  219. /* Remember PTR in 'alloced' so that it can be freed later.  Yield PTR.  */
  220. {
  221.     register struct alloclist *q = talloc(struct alloclist);
  222.     q->nextalloc = alloced;
  223.     alloced = q;
  224.     return q->alloc = ptr;
  225. }
  226.  
  227.     malloc_type
  228. ftestalloc(size)
  229.     size_t size;
  230. /* Allocate a block, putting it in 'alloced' so it can be freed later. */
  231. {
  232.     return fremember(testalloc(size));
  233. }
  234.  
  235.     void
  236. ffree()
  237. /* Free all blocks allocated with ftestalloc().  */
  238. {
  239.     register struct alloclist *p, *q;
  240.     for (p = alloced;  p;  p = q) {
  241.         q = p->nextalloc;
  242.         tfree(p->alloc);
  243.         tfree(p);
  244.     }
  245.     alloced = nil;
  246. }
  247.  
  248.     void
  249. ffree1(f)
  250.     register char const *f;
  251. /* Free the block f, which was allocated by ftestalloc.  */
  252. {
  253.     register struct alloclist *p, **a = &alloced;
  254.  
  255.     while ((p = *a)->alloc  !=  f)
  256.         a = &p->nextalloc;
  257.     *a = p->nextalloc;
  258.     tfree(p->alloc);
  259.     tfree(p);
  260. }
  261.  
  262.     char *
  263. str_save(s)
  264.     char const *s;
  265. /* Save s in permanently allocated storage. */
  266. {
  267.     return strcpy(tnalloc(char, strlen(s)+1), s);
  268. }
  269.  
  270.     char *
  271. fstr_save(s)
  272.     char const *s;
  273. /* Save s in storage that will be deallocated when we're done with this file. */
  274. {
  275.     return strcpy(ftnalloc(char, strlen(s)+1), s);
  276. }
  277.  
  278.     char *
  279. cgetenv(name)
  280.     char const *name;
  281. /* Like getenv(), but yield a copy; getenv() can overwrite old results. */
  282. {
  283.     register char *p;
  284.  
  285.     return (p=getenv(name)) ? str_save(p) : p;
  286. }
  287.  
  288.     char const *
  289. getusername(suspicious)
  290.     int suspicious;
  291. /* Get the caller's login name.  Trust only getwpuid if SUSPICIOUS.  */
  292. {
  293.     static char *name;
  294.  
  295.     if (!name) {
  296.         if (
  297.             /* Prefer getenv() unless suspicious; it's much faster.  */
  298. #            if getlogin_is_secure
  299.                 (suspicious
  300.                 ||
  301.                 !(name = cgetenv("LOGNAME"))
  302.                 &&  !(name = cgetenv("USER")))
  303.             &&  !(name = getlogin())
  304. #            else
  305.             suspicious
  306.             ||
  307.                 !(name = cgetenv("LOGNAME"))
  308.                 &&  !(name = cgetenv("USER"))
  309.                 &&  !(name = getlogin())
  310. #            endif
  311.         ) {
  312. #if has_getuid && has_getpwuid
  313.             struct passwd const *pw = getpwuid(ruid());
  314.             if (!pw)
  315.                 faterror("no password entry for userid %lu",
  316.                      (unsigned long)ruid()
  317.                 );
  318.             name = pw->pw_name;
  319. #else
  320. #if has_setuid
  321.             faterror("setuid not supported");
  322. #else
  323.             faterror("Who are you?  Please set LOGNAME.");
  324. #endif
  325. #endif
  326.         }
  327.         checksid(name);
  328.     }
  329.     return name;
  330. }
  331.  
  332.  
  333.  
  334.  
  335. #if has_signal
  336.  
  337. /*
  338.  *     Signal handling
  339.  *
  340.  * Standard C places too many restrictions on signal handlers.
  341.  * We obey as many of them as we can.
  342.  * Posix places fewer restrictions, and we are Posix-compatible here.
  343.  */
  344.  
  345. static sig_atomic_t volatile heldsignal, holdlevel;
  346.  
  347.     static signal_type
  348. catchsig(s)
  349.     int s;
  350. {
  351.     char const *sname;
  352.     char buf[BUFSIZ];
  353.  
  354. #if sig_zaps_handler
  355.     /* If a signal arrives before we reset the signal handler, we lose. */
  356.     VOID signal(s, SIG_IGN);
  357. #endif
  358.     if (holdlevel) {
  359.         heldsignal = s;
  360.         return;
  361.     }
  362.     ignoreints();
  363.     setrid();
  364.     if (!quietflag) {
  365.         sname = nil;
  366. #if has_sys_siglist && defined(NSIG)
  367.         if ((unsigned)s < NSIG) {
  368. #        ifndef sys_siglist
  369.             extern char const *sys_siglist[];
  370. #        endif
  371.         sname = sys_siglist[s];
  372.         }
  373. #else
  374.         switch (s) {
  375. #ifdef SIGHUP
  376.         case SIGHUP:    sname = "Hangup";  break;
  377. #endif
  378. #ifdef SIGINT
  379.         case SIGINT:    sname = "Interrupt";  break;
  380. #endif
  381. #ifdef SIGPIPE
  382.         case SIGPIPE:    sname = "Broken pipe";  break;
  383. #endif
  384. #ifdef SIGQUIT
  385.         case SIGQUIT:    sname = "Quit";  break;
  386. #endif
  387. #ifdef SIGTERM
  388.         case SIGTERM:    sname = "Terminated";  break;
  389. #endif
  390. #ifdef SIGXCPU
  391.         case SIGXCPU:    sname = "Cputime limit exceeded";  break;
  392. #endif
  393. #ifdef SIGXFSZ
  394.         case SIGXFSZ:    sname = "Filesize limit exceeded";  break;
  395. #endif
  396.         }
  397. #endif
  398.         if (sname)
  399.         VOID sprintf(buf, "\nRCS: %s.  Cleaning up.\n", sname);
  400.         else
  401.         VOID sprintf(buf, "\nRCS: Signal %d.  Cleaning up.\n", s);
  402.         VOID write(STDERR_FILENO, buf, strlen(buf));
  403.     }
  404.     exiterr();
  405. }
  406.  
  407.     void
  408. ignoreints()
  409. {
  410.     ++holdlevel;
  411. }
  412.  
  413.     void
  414. restoreints()
  415. {
  416.     if (!--holdlevel && heldsignal)
  417.         VOID catchsig(heldsignal);
  418. }
  419.  
  420.  
  421. static int const sig[] = {
  422. #ifdef SIGHUP
  423.     SIGHUP,
  424. #endif
  425. #ifdef SIGINT
  426.     SIGINT,
  427. #endif
  428. #ifdef SIGPIPE
  429.     SIGPIPE,
  430. #endif
  431. #ifdef SIGQUIT
  432.     SIGQUIT,
  433. #endif
  434. #ifdef SIGTERM
  435.     SIGTERM,
  436. #endif
  437. #ifdef SIGXCPU
  438.     SIGXCPU,
  439. #endif
  440. #ifdef SIGXFSZ
  441.     SIGXFSZ,
  442. #endif
  443. };
  444. #define SIGS (sizeof(sig)/sizeof(*sig))
  445.  
  446.  
  447. #if has_sigaction
  448.  
  449.     static void
  450.   check_sig(r)
  451.     int r;
  452.   {
  453.     if (r != 0)
  454.         efaterror("signal");
  455.   }
  456.  
  457.     static void
  458.   setup_catchsig()
  459.   {
  460.     register int i;
  461.     sigset_t blocked;
  462.     struct sigaction act;
  463.  
  464.     check_sig(sigemptyset(&blocked));
  465.     for (i=SIGS; 0<=--i; )
  466.         check_sig(sigaddset(&blocked, sig[i]));
  467.     for (i=SIGS; 0<=--i; ) {
  468.         check_sig(sigaction(sig[i], (struct sigaction*)nil, &act));
  469.         if (act.sa_handler != SIG_IGN) {
  470.             act.sa_handler = catchsig;
  471.             act.sa_mask = blocked;
  472.             check_sig(sigaction(sig[i], &act, (struct sigaction*)nil));
  473.         }
  474.     }
  475.   }
  476.  
  477. #else
  478. #if has_sigblock
  479.  
  480.     static void
  481.   setup_catchsig()
  482.   {
  483.     register int i;
  484.     int mask;
  485.  
  486.     mask = 0;
  487.     for (i=SIGS; 0<=--i; )
  488.         mask |= sigmask(sig[i]);
  489.     mask = sigblock(mask);
  490.     for (i=SIGS; 0<=--i; )
  491.         if (
  492.             signal(sig[i], catchsig) == SIG_IGN  &&
  493.             signal(sig[i], SIG_IGN) != catchsig
  494.         )
  495.             faterror("signal catcher failure");
  496.     VOID sigsetmask(mask);
  497.   }
  498.  
  499. #else
  500.  
  501.     static void
  502.   setup_catchsig()
  503.   {
  504.     register i;
  505.  
  506.     for (i=SIGS; 0<=--i; )
  507.         if (
  508.             signal(sig[i], SIG_IGN) != SIG_IGN  &&
  509.             signal(sig[i], catchsig) != SIG_IGN
  510.         )
  511.             faterror("signal catcher failure");
  512.   }
  513.  
  514. #endif
  515. #endif
  516.  
  517.     void
  518. catchints()
  519. {
  520.     static int catching_ints;
  521.     if (!catching_ints) {
  522.         catching_ints = true;
  523.         setup_catchsig();
  524.     }
  525. }
  526.  
  527. #endif /* has_signal */
  528.  
  529.  
  530.     void
  531. fastcopy(inf,outf)
  532.     register RILE *inf;
  533.     FILE *outf;
  534. /* Function: copies the remainder of file inf to outf.
  535.  */
  536. {
  537. #if large_memory
  538. #    if has_mmap
  539.         awrite((char const*)inf->ptr, (size_t)(inf->lim - inf->ptr), outf);
  540.         inf->ptr = inf->lim;
  541. #    else
  542.         for (;;) {
  543.         awrite((char const*)inf->ptr, (size_t)(inf->readlim - inf->ptr), outf);
  544.         inf->ptr = inf->readlim;
  545.         if (inf->ptr == inf->lim)
  546.             break;
  547.         VOID Igetmore(inf);
  548.         }
  549. #    endif
  550. #else
  551.     char buf[BUFSIZ*8];
  552.     register fread_type rcount;
  553.  
  554.         /*now read the rest of the file in blocks*/
  555.     while (!feof(inf)) {
  556.         if (!(rcount = Fread(buf,sizeof(*buf),sizeof(buf),inf))) {
  557.             testIerror(inf);
  558.             return;
  559.         }
  560.         awrite(buf, (size_t)rcount, outf);
  561.         }
  562. #endif
  563. }
  564.  
  565. #ifndef SSIZE_MAX
  566.  /* This does not work in #ifs, but it's good enough for us.  */
  567.  /* Underestimating SSIZE_MAX may slow us down, but it won't break us.  */
  568. #    define SSIZE_MAX ((unsigned)-1 >> 1)
  569. #endif
  570.  
  571.     void
  572. awrite(buf, chars, f)
  573.     char const *buf;
  574.     size_t chars;
  575.     FILE *f;
  576. {
  577.     /* Posix 1003.1-1990 ssize_t hack */
  578.     while (SSIZE_MAX < chars) {
  579.         if (Fwrite(buf, sizeof(*buf), SSIZE_MAX, f)  !=  SSIZE_MAX)
  580.             Oerror();
  581.         buf += SSIZE_MAX;
  582.         chars -= SSIZE_MAX;
  583.     }
  584.  
  585.     if (Fwrite(buf, sizeof(*buf), chars, f)  !=  chars)
  586.         Oerror();
  587. }
  588.  
  589.  
  590.  
  591.  
  592.  
  593.     static int
  594. movefd(old, new)
  595.     int old, new;
  596. {
  597.     if (old < 0  ||  old == new)
  598.         return old;
  599. #    ifdef F_DUPFD
  600.         new = fcntl(old, F_DUPFD, new);
  601. #    else
  602.         new = dup2(old, new);
  603. #    endif
  604.     return close(old)==0 ? new : -1;
  605. }
  606.  
  607.     static int
  608. fdreopen(fd, file, flags)
  609.     int fd;
  610.     char const *file;
  611.     int flags;
  612. {
  613.     int newfd;
  614.     VOID close(fd);
  615.     newfd =
  616. #if !open_can_creat
  617.         flags&O_CREAT ? creat(file, S_IRUSR|S_IWUSR) :
  618. #endif
  619.         open(file, flags, S_IRUSR|S_IWUSR);
  620.     return movefd(newfd, fd);
  621. }
  622.  
  623. #if !has_spawn
  624.     static void
  625. tryopen(fd,file,flags)
  626.     int fd, flags;
  627.     char const *file;
  628. {
  629.     if (file  &&  fdreopen(fd,file,flags) != fd)
  630.         efaterror(file);
  631. }
  632. #else
  633.     static int
  634. tryopen(fd,file,flags)
  635.     int fd, flags;
  636.     char const *file;
  637. {
  638.     int newfd = -1;
  639.     if (file  &&  ((newfd=dup(fd)) < 0  ||  fdreopen(fd,file,flags) != fd))
  640.         efaterror(file);
  641.     return newfd;
  642. }
  643.     static void
  644. redirect(old, new)
  645.     int old, new;
  646. {
  647.     if (0 <= old   &&   (close(new) != 0  ||  movefd(old,new) < 0))
  648.         efaterror("spawn I/O redirection");
  649. }
  650. #endif
  651.  
  652.  
  653.  
  654. #if !has_fork && !has_spawn
  655.     static void
  656. bufargcat(b, c, s)
  657.     register struct buf *b;
  658.     int c;
  659.     register char const *s;
  660. /* Append to B a copy of C, plus a quoted copy of S.  */
  661. {
  662.     register char *p;
  663.     register char const *t;
  664.     size_t bl, sl;
  665.  
  666.     for (t=s, sl=0;  *t;  )
  667.         sl  +=  3*(*t++=='\'') + 1;
  668.     bl = strlen(b->string);
  669.     bufrealloc(b, bl + sl + 4);
  670.     p = b->string + bl;
  671.     *p++ = c;
  672.     *p++ = '\'';
  673.     while (*s) {
  674.         if (*s == '\'') {
  675.             *p++ = '\'';
  676.             *p++ = '\\';
  677.             *p++ = '\'';
  678.         }
  679.         *p++ = *s++;
  680.     }
  681.     *p++ = '\'';
  682.     *p = 0;
  683. }
  684. #endif
  685.  
  686. /*
  687. * Run a command specified by the strings in 'inoutargs'.
  688. * inoutargs[0], if nonnil, is the name of the input file.
  689. * inoutargs[1], if nonnil, is the name of the output file.
  690. * inoutargs[2..] form the command to be run.
  691. */
  692.     int
  693. runv(inoutargs)
  694.     char const **inoutargs;
  695. {
  696.     register char const **p;
  697.     int wstatus;
  698.  
  699.     oflush();
  700.     eflush();
  701.     {
  702. #if has_spawn
  703.     int in, out;
  704.     p = inoutargs;
  705.     in = tryopen(STDIN_FILENO, *p++, O_BINARY|O_RDONLY);
  706.     out = tryopen(STDOUT_FILENO, *p++, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY);
  707.     wstatus = spawn_RCS(0, *p, (char*const*)p);
  708.     if (wstatus == -1  &&  errno == ENOEXEC) {
  709.         *--p = RCS_SHELL;
  710.         wstatus = spawnv(0, *p, (char*const*)p);
  711.     }
  712.     redirect(in, STDIN_FILENO);
  713.     redirect(out, STDOUT_FILENO);
  714. #else
  715. #if has_fork
  716.     pid_t pid;
  717. #    if !has_waitpid
  718.         pid_t w;
  719. #    endif
  720.     if (!(pid = vfork())) {
  721.         p = inoutargs;
  722.         tryopen(STDIN_FILENO, *p++, O_BINARY|O_RDONLY);
  723.         tryopen(STDOUT_FILENO, *p++, O_BINARY|O_CREAT|O_TRUNC|O_WRONLY);
  724.         VOID exec_RCS(*p, (char*const*)p);
  725.         if (errno == ENOEXEC) {
  726.             *--p = RCS_SHELL;
  727.             VOID execv(*p, (char*const*)p);
  728.         }
  729.         VOID write(STDERR_FILENO, *p, strlen(*p));
  730.         VOID write(STDERR_FILENO, ": not found\n", 12);
  731.         _exit(EXIT_TROUBLE);
  732.     }
  733.     if (pid < 0)
  734.         efaterror("fork");
  735. #    if has_waitpid
  736.         if (waitpid(pid, &wstatus, 0) < 0)
  737.             efaterror("waitpid");
  738. #    else
  739.         do {
  740.             if ((w = wait(&wstatus)) < 0)
  741.                 efaterror("wait");
  742.         } while (w != pid);
  743. #    endif
  744. #else
  745.     static struct buf b;
  746.  
  747.     /* Use system().  On many hosts system() discards signals.  Yuck!  */
  748.     p = inoutargs+2;
  749.     bufscpy(&b, *p);
  750.     while (*++p)
  751.         bufargcat(&b, ' ', *p);
  752.     if (inoutargs[0])
  753.         bufargcat(&b, '<', inoutargs[0]);
  754.     if (inoutargs[1])
  755.         bufargcat(&b, '>', inoutargs[1]);
  756.     wstatus = system(b.string);
  757. #endif
  758. #endif
  759.     }
  760.     if (!WIFEXITED(wstatus))
  761.         faterror("%s failed", inoutargs[2]);
  762.     return WEXITSTATUS(wstatus);
  763. }
  764.  
  765. #define CARGSMAX 20
  766. /*
  767. * Run a command.
  768. * The first two arguments are the input and output files (if nonnil);
  769. * the rest specify the command and its arguments.
  770. */
  771.     int
  772. #if has_prototypes
  773. run(char const *infile, char const *outfile, ...)
  774. #else
  775.     /*VARARGS2*/
  776. run(infile, outfile, va_alist)
  777.     char const *infile;
  778.     char const *outfile;
  779.     va_dcl
  780. #endif
  781. {
  782.     va_list ap;
  783.     char const *rgargs[CARGSMAX];
  784.     register i = 0;
  785.     rgargs[0] = infile;
  786.     rgargs[1] = outfile;
  787.     vararg_start(ap, outfile);
  788.     for (i = 2;  (rgargs[i++] = va_arg(ap, char const*));  )
  789.         if (CARGSMAX <= i)
  790.             faterror("too many command arguments");
  791.     va_end(ap);
  792.     return runv(rgargs);
  793. }
  794.  
  795.  
  796.     char const *
  797. date2str(date, datebuf)
  798.     char const date[datesize];
  799.     char datebuf[datesize];
  800. /*
  801. * Format a user-readable form of the RCS format DATE into the buffer DATEBUF.
  802. * Yield DATEBUF.
  803. */
  804. {
  805.     register char const *p = date;
  806.  
  807.     while (*p++ != '.')
  808.         ;
  809.     VOID sprintf(datebuf,
  810.         "19%.*s/%.2s/%.2s %.2s:%.2s:%s" +
  811.             (date[2]=='.' && VERSION(5)<=RCSversion  ?  0  :  2),
  812.         (int)(p-date-1), date,
  813.         p, p+3, p+6, p+9, p+12
  814.     );
  815.     return datebuf;
  816. }
  817.  
  818.  
  819. int RCSversion;
  820.  
  821.     void
  822. setRCSversion(str)
  823.     char const *str;
  824. {
  825.     static int oldversion;
  826.  
  827.     register char const *s = str + 2;
  828.     int v = VERSION_DEFAULT;
  829.  
  830.     if (oldversion)
  831.         redefined('V');
  832.     oldversion = true;
  833.  
  834.     if (*s) {
  835.         v = 0;
  836.         while (isdigit(*s))
  837.             v  =  10*v + *s++ - '0';
  838.         if (*s)
  839.             faterror("%s isn't a number", str);
  840.         if (v < VERSION_min  ||  VERSION_max < v)
  841.             faterror("%s out of range %d..%d", str, VERSION_min, VERSION_max);
  842.     }
  843.  
  844.     RCSversion = VERSION(v);
  845. }
  846.  
  847.     int
  848. getRCSINIT(argc, argv, newargv)
  849.     int argc;
  850.     char **argv, ***newargv;
  851. {
  852.     register char *p, *q, **pp;
  853.     unsigned n;
  854.  
  855.     if (!(q = cgetenv("RCSINIT")))
  856.         *newargv = argv;
  857.     else {
  858.         n = argc + 2;
  859.         /*
  860.          * Count spaces in RCSINIT to allocate a new arg vector.
  861.          * This is an upper bound, but it's OK even if too large.
  862.          */
  863.         for (p = q;  ;  ) {
  864.             switch (*p++) {
  865.                 default:
  866.                 continue;
  867.  
  868.                 case ' ':
  869.                 case '\b': case '\f': case '\n':
  870.                 case '\r': case '\t': case '\v':
  871.                 n++;
  872.                 continue;
  873.  
  874.                 case '\0':
  875.                 break;
  876.             }
  877.             break;
  878.         }
  879.         *newargv = pp = tnalloc(char*, n);
  880.         *pp++ = *argv++; /* copy program name */
  881.         for (p = q;  ;  ) {
  882.             for (;;) {
  883.                 switch (*q) {
  884.                     case '\0':
  885.                     goto copyrest;
  886.  
  887.                     case ' ':
  888.                     case '\b': case '\f': case '\n':
  889.                     case '\r': case '\t': case '\v':
  890.                     q++;
  891.                     continue;
  892.                 }
  893.                 break;
  894.             }
  895.             *pp++ = p;
  896.             ++argc;
  897.             for (;;) {
  898.                 switch ((*p++ = *q++)) {
  899.                     case '\0':
  900.                     goto copyrest;
  901.  
  902.                     case '\\':
  903.                     if (!*q)
  904.                         goto copyrest;
  905.                     p[-1] = *q++;
  906.                     continue;
  907.  
  908.                     default:
  909.                     continue;
  910.  
  911.                     case ' ':
  912.                     case '\b': case '\f': case '\n':
  913.                     case '\r': case '\t': case '\v':
  914.                     break;
  915.                 }
  916.                 break;
  917.             }
  918.             p[-1] = '\0';
  919.         }
  920.         copyrest:
  921.         while ((*pp++ = *argv++))
  922.             ;
  923.     }
  924.     return argc;
  925. }
  926.  
  927.  
  928. #define cacheid(E) static uid_t i; static int s; if (!s){ s=1; i=(E); } return i
  929.  
  930. #if has_getuid
  931.     uid_t ruid() { cacheid(getuid()); }
  932. #endif
  933. #if has_setuid
  934.     uid_t euid() { cacheid(geteuid()); }
  935. #endif
  936.  
  937.  
  938. #if has_setuid
  939.  
  940. /*
  941.  * Setuid execution really works only with Posix 1003.1a Draft 5 seteuid(),
  942.  * because it lets us switch back and forth between arbitrary users.
  943.  * If seteuid() doesn't work, we fall back on setuid(),
  944.  * which works if saved setuid is supported,
  945.  * unless the real or effective user is root.
  946.  * This area is such a mess that we always check switches at runtime.
  947.  */
  948.  
  949.     static void
  950. set_uid_to(u)
  951.     uid_t u;
  952. /* Become user u.  */
  953. {
  954.     static int looping;
  955.  
  956.     if (euid() == ruid())
  957.         return;
  958. #if (has_fork||has_spawn) && DIFF_ABSOLUTE
  959.     if (seteuid(u) != 0)
  960.         efaterror("setuid");
  961. #endif
  962.     if (geteuid() != u) {
  963.         if (looping)
  964.             return;
  965.         looping = true;
  966.         faterror("root setuid not supported" + (u?5:0));
  967.     }
  968. }
  969.  
  970. static int stick_with_euid;
  971.  
  972.     void
  973. /* Ignore all calls to seteid() and setrid().  */
  974. nosetid()
  975. {
  976.     stick_with_euid = true;
  977. }
  978.  
  979.     void
  980. seteid()
  981. /* Become effective user.  */
  982. {
  983.     if (!stick_with_euid)
  984.         set_uid_to(euid());
  985. }
  986.  
  987.     void
  988. setrid()
  989. /* Become real user.  */
  990. {
  991.     if (!stick_with_euid)
  992.         set_uid_to(ruid());
  993. }
  994. #endif
  995.